import { BuilderHTMLElement } from '@farris/designer-element';
import FdContainerBaseComponent from '../../common/containerBase/containerBase';
import { DgControl } from '../../../../utils/dg-control';
import { FormBindingType, DesignViewModelService, SchemaService, DomService, ComponentResolveContext } from '@farris/designer-services';
import { ComponentSchema } from '@farris/designer-element';
import { ControlEventPropertyIDList } from '../../../../utils/control-event-prop';
import { IdService } from '@farris/ui-common';

export class FormDragDropManager {

    private cmpInstance: FdContainerBaseComponent;

    constructor(cmpInstance: FdContainerBaseComponent) {
        this.cmpInstance = cmpInstance;
    }
    /**
     * 判断是否可以接收拖拽新增的子级控件
     * @returns boolean
     */
    canAccepts(sourceElement: BuilderHTMLElement) {

        const serviceHost = this.cmpInstance.options.designerHost;
        const domService = serviceHost.getService('DomService') as DomService;
        const dragResolveService = serviceHost.getService('DragResolveService');
        const resolveContext = dragResolveService.getComponentResolveContext(sourceElement, this) as ComponentResolveContext;;

        // 内部为table控件时，不接收外部控件
        const cmpClass = this.cmpInstance.component.appearance ? this.cmpInstance.component.appearance.class || '' : '';
        const childContents = this.cmpInstance.component.contents || [];
        const firstChildContent = childContents.length ? childContents[0] : null;
        if (cmpClass.includes('f-form-is-table') && firstChildContent && firstChildContent.type === DgControl.Table.type) {
            return false;
        }

        // 只接收输入类、fieldset分组控件、工具箱中的输入类模板
        let allowed = resolveContext.controlCategory === 'input' || resolveContext.controlType === DgControl.FieldSet.type ||
            (resolveContext.controlCategory === 'componentTemplate' && resolveContext.controlTemplate && resolveContext.controlTemplate.templateCategory === 'input');
        if (!allowed) {
            return false;
        }
        // 实体树中拖拽来的字段，需要判断是否属于当前组件绑定的实体
        if (resolveContext.sourceType === 'field') {
            const schemaService = serviceHost.getService('SchemaService') as SchemaService;
            const fieldInfo = schemaService.getFieldByIDAndVMID(resolveContext.bindingTargetId, this.cmpInstance.viewModelId);
            if (!fieldInfo || !fieldInfo.schemaField) {
                return false;
            }
        }
        // 在现有设计器中拖拽控件：跨Component的移动输入控件，需要判断组件是否绑定同一实体。
        const sourceComponentInstance = sourceElement.componentInstance;
        if (sourceComponentInstance && sourceComponentInstance.viewModelId !== this.cmpInstance.viewModelId) {
            if (resolveContext.controlType === DgControl.FieldSet.type) {
                return false;
            }
            const sourceVM = domService.getViewModelById(sourceComponentInstance.viewModelId);
            const currentVM = domService.getViewModelById(this.cmpInstance.viewModelId);
            allowed = sourceVM.bindTo === currentVM.bindTo;

        }


        return allowed;
    }



    /**
     * 移动控件后事件：在可视化设计器中，将现有的控件移动到容器中
     * @param el 移动的源DOM结构
     */
    onAcceptMovedChildElement(sourceElement: BuilderHTMLElement) {
        if (!sourceElement) {
            return;
        }


        const sourceComponentInstance = sourceElement.componentInstance;
        if (!sourceComponentInstance) {
            return;
        }
        const sourceComponentSchema = sourceComponentInstance.component;
        if (!sourceComponentSchema.binding || !sourceComponentSchema.binding.field) {
            return;
        }

        const serviceHost = this.cmpInstance.options.designerHost;
        const domService = serviceHost.getService('DomService') as DomService;
        const dgViewModelService = serviceHost.getService('DesignViewModelService') as DesignViewModelService;
        const sourceDgViewModel = dgViewModelService.getDgViewModel(sourceComponentInstance.viewModelId);

        // 若从fieldSet移动到Form，需要移除VM中的分组信息
        const sourceParentComponent = sourceComponentInstance.parent;
        if (sourceParentComponent && sourceParentComponent.type === DgControl.FieldSet.type) {
            if (sourceDgViewModel) {
                const vmChangeSet = { groupId: null, groupName: null };
                const sourceBindingType = sourceComponentSchema.binding.type;
                switch (sourceBindingType) {
                    case FormBindingType.Form: {
                        sourceDgViewModel.changeField(sourceComponentSchema.binding.field, vmChangeSet);
                        break;
                    }
                    case FormBindingType.Variable: {
                        domService.modifyViewModelFieldById(sourceComponentInstance.viewModelId, sourceComponentSchema.binding.field, vmChangeSet, true);
                        break;
                    }
                }
            }
        }

        // 若是跨组件的移动，需要移动vm的字段
        if (sourceComponentInstance.viewModelId !== this.cmpInstance.viewModelId) {
            this.moveInputBetweenComponent(sourceComponentSchema, sourceComponentInstance.viewModelId);
        }


    }

    /**
     * 跨组件移动输入控件
     * @param sourceComponentSchema 源控件Schema结构
     * @param sourceViewModelId  源控件所属视图模型id
     */
    moveInputBetweenComponent(sourceComponentSchema: ComponentSchema, sourceViewModelId: string) {
        const serviceHost = this.cmpInstance.options.designerHost;
        const domService = serviceHost.getService('DomService') as DomService;
        const dgViewModelService = serviceHost.getService('DesignViewModelService') as DesignViewModelService;

        // 源控件绑定的字段或变量id
        const sourceBindingType = sourceComponentSchema.binding.type;
        const sourceBindingFieldId = sourceComponentSchema.binding.field;

        switch (sourceBindingType) {
            case FormBindingType.Form: {
                this.moveFieldBetweenViewModel(sourceBindingFieldId, sourceViewModelId, dgViewModelService);
                break;
            }
            case FormBindingType.Variable: {
                this.moveVariableBetweenViewModel(sourceBindingFieldId, sourceViewModelId, domService);
                break;
            }
        }
        this.handleVaribleInPropertyBinding(sourceComponentSchema, sourceViewModelId, domService);
        this.handleEventsInProperty(sourceComponentSchema, sourceViewModelId, domService, dgViewModelService);
    }

    /**
     * 若源控件绑定字段，需要将绑定信息移动到新VM
     * @param sourceBindingFieldId 源控件绑定的字段id
     * @param sourceViewModelId 源控件所属视图模型id
     * @param dgViewModelService 视图模型服务类
     */
    private moveFieldBetweenViewModel(sourceBindingFieldId: string, sourceViewModelId: string, dgViewModelService: DesignViewModelService) {
        const sourceDgViewModel = dgViewModelService.getDgViewModel(sourceViewModelId);
        const sourceVMField = sourceDgViewModel.fields.find(f => f.id === sourceBindingFieldId);
        sourceDgViewModel.removeField([sourceBindingFieldId]);

        const currentDgViewModel = dgViewModelService.getDgViewModel(this.cmpInstance.viewModelId);
        currentDgViewModel.addField(sourceVMField);

    }

    /**
     * 源控件绑定变量，需要将绑定信息移动到新VM，并且将绑定变量拷贝到新VM
     * @param sourceBindingFieldId 源控件绑定的字段id
     * @param sourceViewModelId 源控件所属视图模型id
     * @param domService DOM结构服务类
     */
    private moveVariableBetweenViewModel(sourceBindingFieldId: string, sourceViewModelId: string, domService: DomService) {
        const sourceVm = domService.getViewModelById(sourceViewModelId);
        const currentVM = domService.getViewModelById(this.cmpInstance.viewModelId);

        // 移动VM.fields
        const sourceVMFieldIndex = sourceVm.fields.findIndex(f => f.id === sourceBindingFieldId);
        if (sourceVMFieldIndex > -1) {
            currentVM.fields.push(sourceVm.fields[sourceVMFieldIndex]);
            sourceVm.fields.splice(sourceVMFieldIndex, 1);
        }


        // 拷贝绑定的变量：为了防止此变量在源VM其他地方被使用的情况，这里是拷贝变量，而不是剪切
        const sourceState = sourceVm.states.find(state => state.id === sourceBindingFieldId);
        if (sourceState) {
            currentVM.states = currentVM.states ? currentVM.states : [];
            if (currentVM.states.findIndex(s => s.id === sourceState.id) < 0) {
                currentVM.states.push(sourceState);
            }
        }
    }

    /**
     * 若控件的某一个属性绑定的变量（例如控件的必填属性绑定变量），则需要将此变量拷贝的新VM中
     * @param sourceComponentSchema 源控件Schema结构
     * @param sourceViewModelId  源控件所属视图模型id
     * @param domService DOM结构服务类
     */
    private handleVaribleInPropertyBinding(sourceComponentSchema: ComponentSchema, sourceViewModelId: string, domService: DomService) {

        const sourceVm = domService.getViewModelById(sourceViewModelId);
        const currentVM = domService.getViewModelById(this.cmpInstance.viewModelId);

        const variablesToBeCopied = [];
        Object.keys(sourceComponentSchema).forEach(propertyID => {
            if (propertyID === 'binding') {
                return;
            }
            const propertyValue = sourceComponentSchema[propertyID];
            if (propertyValue && typeof (propertyValue) === 'object' && propertyValue.type === FormBindingType.Variable && propertyValue.field) {
                const sourceState = sourceVm.states.find(state => state.id === propertyValue.field);
                if (sourceState) {
                    variablesToBeCopied.push(sourceState);
                }
            }
        });

        if (variablesToBeCopied.length) {
            currentVM.states = currentVM.states ? currentVM.states.concat(variablesToBeCopied) : [].concat(variablesToBeCopied);
        }
    }

    /**
     * 拷贝控件绑定的事件
     * @param sourceComponentSchema 源控件Schema结构
     * @param sourceViewModelId  源控件所属视图模型id
     * @param domService DOM结构服务类
     */
    private handleEventsInProperty(sourceComponentSchema: ComponentSchema, sourceViewModelId: string, domService: DomService, dgViewModelService: DesignViewModelService) {
        const sourceVm = domService.getViewModelById(sourceViewModelId);
        const targetVm = domService.getViewModelById(this.cmpInstance.viewModelId);
        const targetDgViewModel = dgViewModelService.getDgViewModel(this.cmpInstance.viewModelId);

        const eventCodeList = Object.keys(ControlEventPropertyIDList);

        // 控件多个属性绑定一个命令时，不重复创建新命令
        const commandMap = {};

        Object.keys(sourceComponentSchema).forEach(propertyID => {
            const e = eventCodeList.find(key => key === propertyID);
            if (e && sourceComponentSchema[e]) {
                const oldCommand = sourceVm.commands.find(c => c.code === sourceComponentSchema[e]);
                if (oldCommand) {
                    // 新建命令
                    let newCommand = commandMap[oldCommand.code];
                    if (!newCommand) {
                        newCommand = this.createNewCommand(oldCommand, domService);
                        targetVm.commands.push(newCommand);

                        commandMap[oldCommand.code] = newCommand;
                    }

                    // 修改控件绑定的命令
                    sourceComponentSchema[e] = newCommand.code;

                }
            }
        });

        // 字段值变化前、后事件
        const fieldEvents = ['valueChanging', 'valueChanged'];
        if (sourceComponentSchema.binding && sourceComponentSchema.binding.type === FormBindingType.Form) {
            const vmField = targetVm.fields.find(f => f.id === sourceComponentSchema.binding.field);
            if (vmField) {
                fieldEvents.forEach(event => {
                    if (vmField[event]) {
                        const oldCommand = sourceVm.commands.find(c => c.code === vmField[event]);
                        if (!oldCommand) {
                            return;
                        }
                        if (commandMap[oldCommand.code]) {
                            targetDgViewModel.changeField(vmField.id, { [event]: commandMap[oldCommand.code].code });
                        } else {

                            const newCommand = this.createNewCommand(oldCommand, domService);
                            commandMap[oldCommand.code] = newCommand;
                            targetVm.commands.push(newCommand);

                            targetDgViewModel.changeField(vmField.id, { [event]: newCommand.code });
                        }
                    }
                });
            }
        }

    }


    private createNewCommand(oldCommand: any, domService: any) {
        const serviceHost = this.cmpInstance.options.designerHost;
        const injector = serviceHost.getService('Injector');
        // 1、新建命令
        const guiId = injector.get(IdService).generate();
        const newCommand = Object.assign({}, oldCommand, {
            id: guiId,
            code: oldCommand.code + guiId.slice(0, 4),
            name: oldCommand.name + guiId.slice(0, 4)
        });
        // 2、新建的命令添加到webCmds节点下
        const webCmds = domService.getWebCmds();
        const cmp = webCmds.find(cmd => cmd.id === oldCommand.cmpId);
        if (cmp) {
            cmp.refedHandlers.push({
                host: guiId,
                handler: oldCommand.handlerName
            });
        }
        return newCommand;
    }
}
